package org.goodsl.deploymap.tool;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.goodsl.deploymap.*;
import org.goodsl.deploymap.loader.DiskFolderLoader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: Simon Xiao
 * Date: 12-12-20
 * Time: 下午3:16
 * Copyright goodsl.org 2012~2020
 */
public class DotGenerator {

    public static void main(String[] args) throws Exception {
        if (args.length < 3) {
            showHelp();
            return;
        }

        String dsl_path = args[0];
        String template_path = args[1];
        String out_path = args[2];

        File dslFolder = checkPath(dsl_path, false);
        if (dslFolder == null)
            return;
        File templateFolder = checkPath(template_path, false);
        if (templateFolder == null)
            return;
        File outFloder = checkPath(out_path, true);

        if (outFloder == null)
            return;

        DotGenerator generator = new DotGenerator();

        generator.outPath = outFloder.getAbsolutePath() + File.separatorChar;
        generator.imageOutPath = outFloder.getAbsolutePath() + File.separatorChar + "images" + File.separatorChar;
        checkPath(generator.imageOutPath, true);
        generator.templates = new Configuration();
        generator.templates.setDirectoryForTemplateLoading(templateFolder);

        DiskFolderLoader dslLoader = new DiskFolderLoader(dslFolder.getAbsolutePath());
        generator.appSystem = Parser.parse(dslLoader);

        generator.dataCheck();

        generator.generateAll();




    }

    int imageCount ;
    int pageCount;

    /**
     * 因为定义服务关系或部署时可能引用了不存在的服务，会导致图片生成出错
     */
    private void dataCheck(){
        for(Service service : appSystem.getServiceSet()){
            ArrayList<ServiceRelation> relations = service.getRelations();
            ArrayList<ServiceRelation> removeRelations = new ArrayList<ServiceRelation>();
            for (ServiceRelation relation: relations){
                if(!appSystem.hasService(relation.getNameB())){
                    String message = "service [" + service.getName() +
                            "] has relation with service [" +
                            relation.getNameB() +
                            "] which is not exist! relation removed!";
                    System.out.println("Warning: "+ message);
                    removeRelations.add(relation);
                }
            }
            if (removeRelations.size()>0){
                for (ServiceRelation relation: removeRelations){
                    relations.remove(relation);
                }
            }
        }

    }


    String imageOutPath;
    String outPath;
    String imagePre = "images" + File.separatorChar;
    Configuration templates;
    AppSystem appSystem;

    ArrayList<String> imageFileList = new ArrayList<String>();

    private Template getUTF8Template(String name)throws Exception{
        return templates.getTemplate(name,"UTF-8");
    }

    private void generateAll()throws Exception{
        generateImages();
        generatePages();
        System.out.println("generate " + imageCount + " dot files." );
        System.out.println("generate " + pageCount + " html files." );



    }

    private void generatePages() throws Exception {
        pageCount = 0;
        genSimplePage("index");
        genSimplePage("comp_allservice");
        genServicePage();
        genEnvPage();
        genHostPage();

    }

    private void genServicePage() throws Exception {
        Template template = getUTF8Template("comp_single_service.ftl");
        Map root = new HashMap();
        root.put("sys", appSystem);
        for (Service currentService : appSystem.getServiceSet()) {
            root.put("currentService", currentService);
            genPageFile(root, template, "comp_" + currentService.getName() + "_single_service.html");
        }
    }

    private void genEnvPage() throws Exception {
        Template template = getUTF8Template("dep_single_env.ftl");
        Map root = new HashMap();
        root.put("sys", appSystem);
        for (Env env : appSystem.getEnvSet()) {
            root.put("env", env);
            genPageFile(root, template, "dep_" + env.getName() + "_single_env.html");
        }
    }

    public void genHostPage() throws Exception {
        Template template = getUTF8Template("dep_single_host.ftl");
        Map root = new HashMap();
        root.put("sys", appSystem);
        for (Env env : appSystem.getEnvSet()) {
            root.put("env", env);
            for(Host host :env.getHosts()){
                root.put("currentHost",host);
                genPageFile(root, template, "dep_" + env.getName()+"_" + host.getName() + "_single_host.html");
            }

        }

    }

    private void genSimplePage(String name) throws Exception {
        Template template = getUTF8Template(name + ".ftl");
        Map root = new HashMap();
        root.put("sys", appSystem);
        genPageFile(root, template, name + ".html");

    }

    private void generateImages() throws Exception {
        imageCount = 0;
        genAllServiceImage();
        genSingelServiceImage();
        genHostNoServiceImage();
        genHostWithServiceImage();
        genServiceInEnvImage();
        genHostImage();
        genImageScript();

    }

    private void genHostImage() throws Exception{
        Template template = getUTF8Template(imagePre + "single_host_with_service.ftl");
        Map root = new HashMap();
        for (Env env : appSystem.getEnvSet()) {
            for(Host host :env.getHosts()){
                root.put("host",host);
                genDotFile(root, template, env.getName() + "_" + host.getName() + "_host_with_service");
            }

        }
    }

    private void genAllServiceImage() throws Exception {
        Map root = new HashMap();
        root.put("services", appSystem.getServices().values().toArray());

        Template template = getUTF8Template(imagePre + "all_service_with_relation_no_module.ftl");
        genDotFile(root, template, "all_service_with_relation_no_module");
    }

    private void genSingelServiceImage() throws Exception {

        Template template = getUTF8Template(imagePre + "single_service_no_module.ftl");

        for (Service service : appSystem.getServices().values()) {
            Map root = new HashMap();
            root.put("service", service);
            String outFile = service.getName() + "_single_service_no_module";
            genDotFile(root, template, outFile);
        }

    }

    private void genHostNoServiceImage() throws Exception {

        //all_env_host_no_service
        Template template = getUTF8Template(imagePre + "all_env_host_no_service.ftl");
        Map root = new HashMap();
        root.put("envs", appSystem.getEnvs().values());
        String outFile = "all_env_host_no_service";
        genDotFile(root, template, outFile);

        //single_env_host_no_service.ftl
        template = getUTF8Template(imagePre + "single_env_host_no_service.ftl");
        for (Env env : appSystem.getEnvs().values()) {
            root = new HashMap();
            root.put("env", env);

            outFile = env.getName() + "_env_host_no_service";
            genDotFile(root, template, outFile);
        }
    }

    private void genHostWithServiceImage() throws Exception {
        //single_env_host_with_service.ftl
        Template t1 = getUTF8Template(imagePre + "single_env_host_with_service.ftl");
        Template t2 = getUTF8Template(imagePre + "single_env_host_with_service_relation.ftl");


        Map root = new HashMap();
        for (Env env : appSystem.getEnvs().values()) {
            root = new HashMap();
            root.put("env", env);

            genDotFile(root, t1, env.getName() + "_env_host_with_service");
            genDotFile(root, t2, env.getName() + "_env_host_with_service_relation");
        }
    }

    private void genServiceInEnvImage() throws Exception {
        Template t1 = getUTF8Template(imagePre + "single_service_in_env.ftl");
        for (Env env : appSystem.getEnvs().values()) {
            Map root = new HashMap();
            root.put("env", env);
            for (Service service : appSystem.getServiceSet()) {
                root.put("serviceName", service.getName());
                genDotFile(root, t1, service.getName() + "_service_in_env_" + env.getName());
            }
        }
    }

    private void genDotFile(Map root, Template template, String outFileName) throws Exception {

        FileOutputStream fos = new FileOutputStream(imageOutPath + outFileName + ".dot");
        OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");

        //System.out.print(outFileName + " ... ");
        template.process(root, writer);
        writer.flush();
        //System.out.println( "over." );
        imageFileList.add(outFileName);
        imageCount++;
    }

    private void genImageScript() throws Exception {
        Map root = new HashMap();
        root.put("imagefileNames", imageFileList);
        Template template = getUTF8Template(imagePre + "genImageScript.ftl");
        FileWriter writer = new FileWriter(imageOutPath + "makeImages.bat");
        template.process(root, writer);
        writer.flush();
        System.out.println("makeImages.bat generated.");
    }

    private void genPageFile(Map root, Template template, String outFileName) throws Exception {

        FileOutputStream fos = new FileOutputStream(outPath + outFileName);
        OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");

        template.process(root, writer);
        writer.flush();


        pageCount++;

    }


    private static void showHelp() {
        System.out.println("Usage: java org.goodsl.deploymap.tool.DotGenerator dsl_path,template_path,out_path");
    }

    private static File checkPath(String path, boolean noExistCreate) {

        File file = new File(path);
        if (!file.exists()) {
            if (noExistCreate) {
                file.mkdir();
            } else {
                System.out.println("'" + path + "' does not exist.");
                return null;
            }
        }
        if (!file.isDirectory()) {
            System.out.println("'" + path + "' is not directory.");
            return null;

        }

        return file;
    }
}
